﻿using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using Newtonsoft.Json;
using SF.Code.Extension.ControlsHelper;
using SF.Code.Extension.DebuggingHelper;
using SF.Code.Extension.HttpHelper.Mime;
using SF.Code.Extension.ObjectHelper;
using SF.Code.Extension.WinAPI;
using SF.Code.Extension.WinAPI.Docking;
using SF.Code.Extension.WinAPI.Keyboard;
using SG.Productivity.DesktopControlPanel.ParallelQuery;
using SG.Productivity.DesktopControlPanel.Properties;
using SG.Productivity.DesktopControlPanel.Support;
using System.Security.Principal;

namespace SG.Productivity.DesktopControlPanel.Controls
{
    public partial class TopPanel : PanelForm
    {
#pragma warning disable 612,618
        // HACK: Works on my machine (TM)
        private static readonly IWebProxy NoProxy = GlobalProxySelection.GetEmptyWebProxy();
#pragma warning restore 612,618
        private WebClient _client;
        private SearchResultInfoPanel _infoPanel;

        public TopPanel()
        {
            InitializeComponent();

            RegisterAppToWindows();
        }

        private void RegisterAppToWindows()
        {
            if (DesignMode) return;

            var moduleName = Process.GetCurrentProcess().MainModule.ModuleName;

            var software = Registry.CurrentUser.OpenSubKey("Software");
            Debug.Assert(software != null, "software != null");

            var classes = software.OpenSubKey("Classes");
            Debug.Assert(classes != null, "classes != null");

            var applications = classes.OpenSubKey("Applications", true);
            Debug.Assert(applications != null, "applications != null");

            var currentApp = applications.OpenSubKey(moduleName, true) ?? applications.CreateSubKey(moduleName);

            var clients = software.OpenSubKey("Clients");
            Debug.Assert(clients != null, "clients != null");
            var startMenuInternet = clients.OpenSubKey("StartMenuInternet", true);
            Debug.Assert(startMenuInternet != null, "startMenuInternet != null");

            var currentClient = startMenuInternet.OpenSubKey(moduleName, true) ?? startMenuInternet.CreateSubKey(moduleName);

            // RecentDocs.SHAddToRecentDocs(ShellAddRecentDocs.SHARD_PATHW, @"B:\Wallpaper_2.jpg");
        }
        
        private bool? _hasHookInstalled;

        private void TopPanel_Load(object sender, EventArgs e)
        {
            var screen = Screen.PrimaryScreen;
            var hookInstalled = InstallMouseHook(screen);
            SetCollapsed(CollapsedOnStart || hookInstalled);
            _infoPanel = new SearchResultInfoPanel();
            _infoPanel.SetVisibility(this, keySet: new NavigationKeySet(backKey: Keys.Up, showKey: Keys.Down, modifier: Keys.Alt), visible: false);

            _infoPanel.AdjustInfoPanel(this.AdjustPosition(screen));

            _client = new WebClient();
            _client.DownloadStringCompleted += ClientOnDownloadStringCompleted;

            ConnectToTAPI();
        }

        private void ConnectToTAPI()
        {
            if (DesignMode) return;
            var useDebugString = DebuggerEx.HasAttachedDebugger(false);
            var uri = new Uri(useDebugString ? Resources.TapiConnectionStringDebug : Resources.TapiConnectionStringRelease);
            socketIOClientComponent1.Connect(uri);
        }

        private bool InstallMouseHook(Screen screen)
        {
            if (_hasHookInstalled.HasValue)
                return _hasHookInstalled.Value;

#if DEBUG
            _hasHookInstalled = !DebuggerEx.HasAttachedDebugger(false);
            if (_hasHookInstalled == false)
                return false;
#else
            _hasHookInstalled = true;
#endif

            MouseHookHelper.Toggle(lowLevelCallback: MouseMoved);
            new {title = "Mouse Hook installed", screen}.Log();

            return _hasHookInstalled.Value;
        }

        private static readonly UriConverter UriConverter = new UriConverter();

        private void ClientOnDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Cancelled)
                return;

            var error = e.Error;
            
            if (error != null)
            {
                error.Log(@break: false);
                return;
            }

            try
            {
                var queryReply = JsonConvert.DeserializeObject<QueryReply>(e.Result, UriConverter);

                if (queryReply.AcceptedProviders == null || queryReply.AcceptedProviders.Length == 0)
                {
                    new {title = "No matching providers", queryReply, lastQuery = _lastQuery}.Log();
                }
                else if (queryReply.Query == _lastQuery)
                {
                    foreach (var specificQuery in queryReply.ProviderSpecificQueries())
                        providerQueryWebClientBag1.AddToDownloadQueue(specificQuery);

                    this.InvokeEx(
                        _ =>
                            {
                                new { title = "Successful HTTP Response", queryReply }.Log();
                                _infoPanel.SetVisibility(activate: PanelActivation.Caller);
                            });
                }
                else
                {
                    new {title = "Timeout HTTP Response", queryReply, lastQuery = _lastQuery}.Log();
                }
            }
            catch (Exception exception)
            {
                exception.Log(@break: false);
            }
        }

        private bool _isEmpty = true;
        private void MouseMoved(ref MouseLowLevelHookStruct data)
        {
            var x = data.pt.x;
            var y = data.pt.y;
            Action<TopPanel> action;
            if (y >= UncollapsedWidth && _isEmpty && !Collapsed)
            {
                action = pPanel => pPanel.HidePanel();
            }
            else if (y <= 1 && x > 40 && x < 300)
            {
                action = pPanel => pPanel.ShowPanel();
            }
            else
            {
                action = null;
            }

            if (action !=null)
                this.InvokeEx(action);
        }

        public bool Collapsed { get; private set; }

        private void TopPanel_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                MouseHookHelper.Unregister();
            }
            catch (Exception exception)
            {
                exception.Log();
            }

            try
            {
                socketIOClientComponent1.Disconnect();
            }
            catch (Exception exception)
            {
                exception.Log();
            }
        }

        private void buttonClose_Click(object sender, System.EventArgs e)
        {
            Close();
        }

        private void textBoxSearch_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            var isSpecial = IsSpecialKeyCode(e.KeyCode);
            if (isSpecial)
                e.IsInputKey = true;

            if (!Collapsed)
            {
                this.InvokeEx(pPanel => pPanel.ShowPanel());
            }
            else if (isSpecial && e.KeyCode == Keys.Escape)
            {
                this.InvokeEx(pPanel => pPanel.HidePanel());
            }
        }

        private void textBoxSearch_TextChanged(object sender, System.EventArgs e)
        {
            _isEmpty = string.IsNullOrWhiteSpace(textBoxSearch.Text);
            if (_isEmpty)
            {
                timerAutocomplete.Enabled = false;
                _infoPanel.InvokeEx(pPanel => pPanel.SetVisibility(visible: false));
                return;
            }

            timerAutocomplete.Enabled = true;
        }

        /*
         * TODO: Use new version of the download manager
         * 
            var webRequest = (HttpWebRequest)WebRequest.Create("http://thewebsite.com/thepage.html");
            using (var requestStream = new StreamWriter(webRequest.GetRequestStream(), encoding: Encoding.UTF8))
            {
                requestStream.Write();   
            }
         */

        private void textBoxSearch_KeyDown(object sender, KeyEventArgs e)
        {
            if (IsSpecialKeyCode(e.KeyCode))
            {
                new {title = "Key Down", @event = e}.Log();
                e.Handled = true;
                e.SuppressKeyPress = true;
                _infoPanel.ActivateItem(new DataGridCell(0, 0));
            }
        }

        private static bool IsSpecialKeyCode(Keys code)
        {
            return code == Keys.Enter | code == Keys.Tab | code == Keys.Escape | code == Keys.Up | code == Keys.Down;
        }

        private string _lastQuery;

        private void TimerAutocompleteTick(object sender, EventArgs e)
        {
            var query = textBoxSearch.Text;

            if (query == _lastQuery)
            {
                timerAutocomplete.Enabled = false;
                return;
            }
            if (_client.IsBusy)
            {
                return;
            }
            _lastQuery = query;

            WindowsIdentity user;
            UserInfo info;
            var uri = new StrongNameValueCollection
                          {
                              {"q", query},
                              {"user", UserInfo.TryGetCurrentUser(out user, out info) ? user.Name : null}
                          }.ToUri(Program.DefaultAddress, path: "search");

            _client.Encoding = Encoding.UTF8;
            _client.Proxy = NoProxy;
            _client.Headers.Add(HttpRequestHeader.Accept, TypeSubPair.Json.ToString());
            _client.DownloadStringAsync(uri);
            new {title = "Initiating download", uri}.Log();
        }

        private void providerQueryWebClientBag1_ErrorOccurred(object sender, UnhandledExceptionEventArgs args)
        {
            args.Log(@break: false);
        }

        private void providerQueryWebClientBag1_DownloadCompleted_1(object sender, ProviderQueryResultEventArgs args)
        {
            var record = args.Result.Data;
            new {args.Result.Key, Data = record, args.IsFromCache}.Log();

            _infoPanel.InvokeEx(pPanel => pPanel.MergeOrderAndDisplayResults(args.Result.Key, record));
        }

        private void TopPanel_CollapsedChanged(object sender, CollapsedChangedEventArgs e)
        {
            if (!e.Current && e.Previous)
                TopMost = true;
        }

        private void hotkeyBinding1_HotKeyPressed(object sender, HotKeyBindingEventArgs e)
        {
            this.ShowPanel();
            Activate();
        }

        private void TopPanel_Activated(object sender, EventArgs e)
        {
            this.ShowPanel();
        }
    }
}